/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package Entidades.promethee;

import Entidades.promethee.funcionPreferencia.*;

/**
 *
 * @author Gabriel
 */
public abstract class Promethee {
    private double valores[][], pesos[];
    private int tipo[], funcionPreferencia[];
    private double p[], q[], sigma[];

    // TIPO DE ALTERNATIVA
    public static final int TIPO_MAXIMIZANTE= 0;
    public static final int TIPO_MINIMIZANTE= 1;

    // FUNCIONES DE PREFERENCIA
    private final int FPREF_CRITERIOUSUAL= 1;
    private final int FPREF_CUASICRITERIO= 2;
    private final int FPREF_SEUDOCRITERIO= 3;
    private final int FPREF_LEVELCRITERIO= 4;
    private final int FPREF_LINEALCONAREAPREFERENCIA= 5;
    private final int FPREF_CRITERIOGAUSSIANO= 6;

    public Promethee(double valores[][], double pesos[], int tipo[], int funcionPreferencia[], double p[], double q[], double sigma[]){
        this.valores= valores;
        this.pesos= pesos;
        this.tipo= tipo;
        this.funcionPreferencia= funcionPreferencia;
        this.p= p;
        this.q= q;
        this.sigma= sigma;
    }

    /***************************** PRIMER PASO ********************************/
    public double[][] calcularDiferencias(){
        double matrizDiferencias[][]= new double[(valores.length * valores.length) - valores.length][valores[0].length];
        
        for(int col= 0; col<valores[0].length; col++){
            int h= 0;
            for(int fila= 0; fila<valores.length; fila++){
                double aux= valores[fila][col];
                for(int filaAux= 0; filaAux<valores.length; filaAux++){
                    if(fila == filaAux) continue;
                    matrizDiferencias[h][col]= (tipo[col] == TIPO_MAXIMIZANTE && aux >= valores[filaAux][col]) || (tipo[col] == TIPO_MINIMIZANTE && aux <= valores[filaAux][col])? Math.abs(aux - valores[filaAux][col]) : 0;
                    h++;
                }
            }
        }

        return matrizDiferencias;
    }

    public double[][] calcularFuncionPreferencia(double matrizDiferencias[][]){
        double matrizFuncionPreferencia[][]= new double[matrizDiferencias.length][matrizDiferencias[0].length];
        FuncionPreferencia funcion= null;

        for(int col= 0; col<matrizDiferencias[0].length; col++){
            funcion= obtenerFuncion(col);
            for(int fila= 0; fila<matrizDiferencias.length; fila++){
                matrizFuncionPreferencia[fila][col]= funcion.aplicarFuncion((float)matrizDiferencias[fila][col]);
            }
        }

        return matrizFuncionPreferencia;
    }

    /***************************** SEGUNDO PASO *******************************/
    public double[][] calcularIndicesPreferencias(double matrizPreferencia[][]){
        double indicesPreferencia[][]= new double[valores.length][valores.length];

        double indicePref;
        int filaAux= 0, colAux= 0;
        for(int fila= 0; fila<matrizPreferencia.length; fila++){
            indicePref= 0;
            for(int col= 0; col<matrizPreferencia[fila].length; col++)
                indicePref+= matrizPreferencia[fila][col] * pesos[col];

            if(filaAux == colAux) colAux++;

            //indicesPreferencia[filaAux][colAux]= indicePref;
            indicesPreferencia[filaAux][colAux]= indicePref/10;

            colAux++;
            if(colAux == indicesPreferencia.length){
                filaAux++;
                colAux= 0;
            }
        }

        return indicesPreferencia;
    }

    /***************************** TERCER PASO ********************************/
    public double[] calcularFlujoSuperacionPositivo(double indicesPreferencia[][]){
        double flujoPositivo[]= new double[indicesPreferencia.length];

        double val;
        for(int i= 0; i<indicesPreferencia.length; i++){
            val= 0;
            for(int j= 0; j<indicesPreferencia[i].length; j++){
                val+= indicesPreferencia[i][j];
            }
            flujoPositivo[i]= val;
        }

        return flujoPositivo;
    }

    public double[] calcularFlujoSuperacionNegativo(double indicesPreferencia[][]){
        double flujoNegativo[]= new double[indicesPreferencia.length];

        double val;
        for(int j= 0; j<indicesPreferencia.length; j++){
            val= 0;
            for(int i= 0; i<indicesPreferencia.length; i++){
                val+= indicesPreferencia[i][j];
            }
            flujoNegativo[j]= val;
        }

        return flujoNegativo;
    }

    /***************************** CUARTO PASO ********************************/
    /**
     * Obtiene el preorden de las alternativas y las ordena por importancia en un
     * vector de pares de valores Alternativa - Valor.
     * @param flujoPositivo - Flujo de superación positivo
     * @param flujoNegativo - Flujo de superación negativo
     * @return vector de pares alternativa - valor
     */
    public abstract double[] obtenerPreorden(double flujoPositivo[], double flujoNegativo[]);

    /************************ MÉTODOS PROTEGIDOS ********************************/
    protected double[] ordenar(double[] preorden){
        for(int i= 1; i<preorden.length - 1; i+=2){
            for(int j= i + 2; j<preorden.length; j+=2){
                if(preorden[i] < preorden[j]){
                    double aux= preorden[i];
                    preorden[i]= preorden[j];
                    preorden[j]= aux;

                    double auxPos= preorden[i-1];
                    preorden[i-1]= preorden[j-1];
                    preorden[j-1]= auxPos;
                }
            }
        }
        return preorden;
    }

    /************************ MÉTODOS PRIVADOS ********************************/
    private FuncionPreferencia obtenerFuncion(int pos){
        switch(funcionPreferencia[pos]){
            case FPREF_CRITERIOUSUAL: return new FPrefCriterioUsual(p[pos], q[pos], sigma[pos]);
            case FPREF_CUASICRITERIO: return new FPrefCuasiCriterio(p[pos], q[pos], sigma[pos]);
            case FPREF_SEUDOCRITERIO: return new FPrefSeudoCriterio(p[pos], q[pos], sigma[pos]);
            case FPREF_LEVELCRITERIO: return new FPrefLevelCriterio(p[pos], q[pos], sigma[pos]);
            case FPREF_LINEALCONAREAPREFERENCIA: return new FPrefCriterioLinealAreaIndiferencia(p[pos], q[pos], sigma[pos]);
            case FPREF_CRITERIOGAUSSIANO: return new FPrefCriterioGaussiano(p[pos], q[pos], sigma[pos]);
        }
        return null;
    }
}
